/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once
#include <baja/transition_tracking/tracked_object.h>
#include <baja/model/generic_model.h>
#include <journal/models/interfaces.h>
#include <journal/models/internal_interfaces.h>
#include <journal/models/ink_stroke_model_factory.h>

namespace journal { namespace model {

namespace b = baja;
namespace bm = baja::model;
namespace bt = baja::transition_tracking;

class ink_surface_model :
    public bt::tracked_object,
    public bm::igeneric_model_observer,
    public iink_surface_model,
    public iinternal_ink_surface_model,
    public std::enable_shared_from_this<iink_surface_model>
{
public:
    // constructor
    ink_surface_model(
        const b::uint32& typeToken,
        const std::shared_ptr<ipage_model>& parent,
        std::shared_ptr<journal::model::db::iink_surface_table> inkSurfaceTable,
        const b::uint32 inkSurfaceId
        );
    static std::shared_ptr<ink_surface_model> create(
        const b::uint32& typeToken,
        const std::shared_ptr<ipage_model>& parent,
        std::shared_ptr<journal::model::db::iink_surface_table> inkSurfaceTable,
        const b::uint32 inkSurfaceId);

    static void removeFromDB(
        const std::shared_ptr<journal::model::db::iink_surface_table>& inkSurfaceTable,
        const b::uint32& pageId);

    // iink_surface_model

    b::int32 addObserver(const std::shared_ptr<iink_surface_model_observer>& observer);
    void removeObserver(const std::shared_ptr<iink_surface_model_observer>& observer);
    void removeObserver(const b::int32 cookie);

    b::uint32 storageId();
    std::shared_ptr<ipage_model> parent();

    void attachInkStroke(const std::shared_ptr<iink_stroke_model>& stroke);
    std::shared_ptr<iink_stroke_collection> inkStrokes();
    std::shared_ptr<iink_stroke_model> createInkStroke();
    void removeInkStroke(const b::uint32& id);

    DEBUG_MODEL_DECLARATION;

    // iinternal_ink_surface_model

    b::uint32 sizeInMemory();
    void initialize();
    void load(b::uint32 pass, bool& done);
    void unload();
    void createInDB();
    void dispose();

    // igeneric_model_observer
    void onChildChanged(
        const b::uint32& typeToken,
        const b::uint32& genericIdentity
        );

    // destructor
    ~ink_surface_model();
private:

    // state
    b::Boolean _inDB;
    b::Boolean _initialized;
    b::Boolean _disposed;
    std::recursive_mutex _mutex;

    // persistence support
    std::shared_ptr<journal::model::db::iink_surface_table> _inkSurfaceTable;

    static const baja::uint32 _inkSurfaceCreatePass;

    // persistence support for children
    static const baja::uint32 _inkStrokesCreatePass;
    static const baja::uint32 _inkStrokesReadyPass;
    bool _inkStrokesIdsReady;

    // tree data structure support
    std::weak_ptr<ipage_model> _parent;

    std::shared_ptr<iink_stroke_model_factory> _inkStrokeFactory;
    concurrency::concurrent_vector<std::shared_ptr<iink_stroke_model>> _strokes;
    b::concurrent_vector_collection<std::shared_ptr<iink_stroke_model>> _strokeCollection;
    b::uint32 _allInkStrokesBlobSize;

    // observers
    b::observable_source<iink_surface_model_observer> _observerSource;

    // methods
    b::Boolean isAccessible();
    void loadSelf();

    void createInkStrokes();
    void loadInkStrokes(b::uint32 pass, bool& allDone);
    void uncreateInkStrokes();

    void observeInkStroke(
        const std::shared_ptr<iink_stroke_model>& inkStroke);

    void unobserveInkStroke(
        const std::shared_ptr<iink_stroke_model>& inkStroke);

    void saveInkStroke(const std::shared_ptr<iink_stroke_model>& stroke);
    void saveInkStrokeAsync(const std::shared_ptr<iink_stroke_model>& stroke);
};

std::shared_ptr<iink_surface_model> createInkSurface(
    const b::uint32& typeToken,
    const std::shared_ptr<ipage_model>& parent,
    std::shared_ptr<journal::model::db::iink_surface_table> inkSurfaceTable,
    const b::uint32 inkSurfaceId);

void removeInkSurfaces(
    const std::shared_ptr<journal::model::db::iink_surface_table>& inkSurfaceTable,
    const b::uint32& pageId);

}} // namespace journal::model